/*
 * oled_spi_sh1106.c
 *
 *  Created on: 2017年1月6日
 *      Author: gavini
 */

/*
 * 显存格式如下：
 * 128*64bit的显存
 * 每页为128个字节，共8页
 * [0] 0 1 2 3 4 ... 127
 * [1] 0 1 2 3 4 ... 127
 * [2] 0 1 2 3 4 ... 127
 * [3] 0 1 2 3 4 ... 127
 * [4] 0 1 2 3 4 ... 127
 * [5] 0 1 2 3 4 ... 127
 * [6] 0 1 2 3 4 ... 127
 * [7] 0 1 2 3 4 ... 127
 *
 * 如果我们使用只写方式操作模块，那么，每次要写8个点，这样，我们在画点的时候，就必须把要设置的点
 * 所在的字节的每个位都搞清楚当前的状态（0/1？），否则写入的数据就会覆盖掉之前的状态，结果就是有
 * 些不需要显示的点，显示出来了，或者该显示的没有显示了。这个问题在能读的模式下，我们可以先读出来
 * 要写入的那个字节，得到当前状况，在修改了要改写的位之后再写进GRAM，这样就不会影响到之前的状况了。
 * 但是这样需要能读GRAM，对于3线或4线SPI模式，模块是不支持读的，而且读->改->写的方式速度也比较慢
 *
 * 所以在内部建立一个OLED的GRAM，在每次修改的时候，只是修改GRAM（实际上就是SRAM）
 * 在修改完了之后，一次性把GRAM写入到OLED的GRAM。当然这个方法也有坏处，就是对于那些SRAM很小
 * 的单片机（比如51系列）就比较麻烦了
 *
 */
#include "oled_spi_sh1106.h"
#include "oled_font.h"
#include "oled_spi_drv.h"
#include <unistd.h>

extern spiPinDriver *pt_spiPinDrv;
extern u8 oled_gram[128][8];


/*
 * DC 命令/数据  0:命令 1:数据
 * SPI模式下，每个数据长度为8位，在sclk的上升沿数据从SDIN移入，并且高位在前
 */
void write_cmd(u8 data)
{
	u8 i;
	pt_spiPinDrv->setCmd();

	for(i=0;i<8;i++){
        pt_spiPinDrv->clrSclkPin();

        if(data&0x80)
        	pt_spiPinDrv->setSdinPin();
		else
			pt_spiPinDrv->clrSdinPin();

        pt_spiPinDrv->setSclkPin();
        data<<=1;
	}
}

/*
 *
 */
void write_data(u8 data)
{
	u8 i;
	pt_spiPinDrv->setData();


	for(i=0;i<8;i++){
		pt_spiPinDrv->clrSclkPin();
		if(data&0x80)
			pt_spiPinDrv->setSdinPin();
		else
			pt_spiPinDrv->clrSdinPin();

		pt_spiPinDrv->setSclkPin();
		data<<=1;
	}
}

/*
 * 复位
 */
void oled_por()
{
	pt_spiPinDrv->setRstPin();
	/*
	 * 调用了系统定时器，因此如果使用usleep、sleep等系统函数需要对AMP下定时器设置正确
	 * 暂时删除usleep 30ms操作
	 */
	//usleep(30000);		// 30ms
	pt_spiPinDrv->clrRstPin();
	//usleep(30000);		// 30ms
	pt_spiPinDrv->setRstPin();
}

/*
 * 初始化操作
 */
void oled_init()
{	oled_por();
	write_cmd(0xAE);//--turn off oled panel
	write_cmd(0x02);//---set low column address
	write_cmd(0x10);//---set high column address
	write_cmd(0x40);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
	write_cmd(0x81);//--set contrast control register
	write_cmd(0xCF); // Set SEG Output Current Brightness
	write_cmd(0xA1);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常
	write_cmd(0xC0);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
	write_cmd(0xA6);//--set normal display
	write_cmd(0xA8);//--set multiplex ratio(1 to 64)
	write_cmd(0x3f);//--1/64 duty
	write_cmd(0xD3);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
	write_cmd(0x00);//-not offset
	write_cmd(0xd5);//--set display clock divide ratio/oscillator frequency
	write_cmd(0x80);//--set divide ratio, Set Clock as 100 Frames/Sec
	write_cmd(0xD9);//--set pre-charge period
	write_cmd(0xF1);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	write_cmd(0xDA);//--set com pins hardware configuration
	write_cmd(0x12);
	write_cmd(0xDB);//--set vcomh
	write_cmd(0x40);//Set VCOM Deselect Level
	write_cmd(0x20);//-Set Page Addressing Mode (0x00/0x01/0x02)
	write_cmd(0x02);//
	write_cmd(0x8D);//--set Charge Pump enable/disable
	write_cmd(0x14);//--set(0x10) disable
	write_cmd(0xA4);// Disable Entire Display On (0xa4/0xa5)
	write_cmd(0xA6);// Disable Inverse Display On (0xa6/a7)
	write_cmd(0xAF);//--turn on oled panel display on

}

/*
 * refresh gram to oled
 */
void oled_refresh_gram(void)
{
	u8 i,n;
	for(i=0;i<8;i++){
		write_cmd (0xb0+i);    //设置页地址（0~7）
		write_cmd (0x02);      //设置显示位置—列低地址,偏移了2列
		write_cmd (0x10);      //设置显示位置—列高地址
		for(n=0;n<128;n++)
			write_data(oled_gram[n][i]);
	}
}

/*
 * 清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
 */
void oled_clear(void)
{
	u8 i,n;
	for(i=0;i<8;i++)
		for(n=0;n<128;n++)
			oled_gram[n][i]=0X00;
	oled_refresh_gram();
}

/*
 * 点
 * x:0~127
 * y:0~63
 * t:1 填充 0,清空
 * ret 1: 超范围
 */
u8 oledDrawPoint(u8 x,u8 y,u8 t)
{
	u8 pos,bx,temp=0;
	if(x>127||y>63)
		return 1;//超出范围了.
	pos=7-y/8;
	bx=y%8;
	temp=1<<(7-bx);

	if(t)
		oled_gram[x][pos]|=temp;
	else
		oled_gram[x][pos]&=~temp;

	return 0;
}

/*
 * x1,y1,x2,y2 填充区域的对角坐标
 * 确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63
 * dot:0,清空;1,填充
 */
void oled_fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)
{
	u8 x,y;
	for(x=x1;x<=x2;x++)
	{
		for(y=y1;y<=y2;y++)
			oledDrawPoint(x,y,dot);
	}
	oled_refresh_gram();//更新显示
}

/*
 *在指定位置显示一个字符,包括部分字符
 *x:0~127
 *y:0~63
 *mode:0,反白显示;1,正常显示
 *size:选择字体 16/12
 */
void oled_showchar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
{
	u8 temp,t,t1;
	u8 y0=y;
	chr=chr-' ';//得到偏移后的值

	for(t=0;t<size;t++){
		if(size==12)
			temp=asc2_1206[chr][t];  //调用1206字体
		else
			temp=asc2_1608[chr][t];		 //调用1608字体

		for(t1=0;t1<8;t1++){
			if(temp&0x80)
				oledDrawPoint(x,y,mode);
			else
				oledDrawPoint(x,y,!mode);

			temp<<=1;
			y++;
			if((y-y0)==size){
				y=y0;
				x++;
				break;
			}
		}
	}
}

//m^n函数
u32 mypow(u8 m,u8 n)
{
	u32 result=1;
	while(n--)result*=m;
	return result;
}

/*
 * 显示数字
 * 	x,y :起点坐标
 * 	num:数值(0~4294967295);
 * 	size：12：位1206字体，其他为1608字体
 */
void oled_shownum(u8 x,u8 y,u32 num,u8 len,u8 size,u8 mode)
{
	u8 t,temp;
	u8 enshow=0;
	for(t=0;t<len;t++){
		temp=(num/mypow(10,len-t-1))%10;

		if(enshow==0&&t<(len-1)){
			if(temp==0){
				oled_showchar(x+(size/2)*t,y,' ',size,mode);
				continue;
			}else
				enshow=1;
		}
		oled_showchar(x+(size/2)*t,y,temp+'0',size,mode);
	}
}

/*
 * 显示字符串
 * x,y:起点坐标
 * *p:字符串起始地址
 * 用16字体
 *
 */

void oled_showstring(u8 x,u8 y,char *p,u8 mode)
{
	#define MAX_CHAR_POSX 128
	#define MAX_CHAR_POSY 64
	while(*p!='\0')
	{
		if(x>MAX_CHAR_POSX)
			{x=0;y+=12;}
		if(y>MAX_CHAR_POSY)
			{y=x=0;oled_clear();}

		oled_showchar(x,y,*p,12,mode);
		x+=6;
		p++;
	}
}

/** 
  * @name	void OLED_Display_On(void)
  * @brief  开启OLED显示
  * @param	none
  * @retval	none
  */ 
void oledDisplayOn(void)
{
	write_cmd(0X8D);	//SET DCDC命令
	write_cmd(0X14);	//DCDC ON
	write_cmd(0XAF);	//DISPLAY ON
}
/** 
  * @name	void OLED_Display_Off(void)
  * @brief  关闭OLED显示
  * @param	none
  * @retval	none
  */ 
void oledDisplayOff(void)
{
	write_cmd(0X8D);	//SET DCDC命令
	write_cmd(0X10);	//DCDC OFF
	write_cmd(0XAE);	//DISPLAY OFF
}

/** 
  * @name	void OLED_DrawLine(u8 x, u8 y, u8 t)
  * @brief  画水平线
  * @param	x0: 0~127	y: 0~63		x1:	0~127	t:1 填充 0 清空
  * @retval	none
  */
void oledDrawLine(u8 x0, u8 y, u8 x1, u8 t)
{
	 u8 x_0, x_1, i;
	if (x0 > x1)	//判断x0与x1的大小
	{
		x_0 = x1;
		x_1 = x0;
	}
	else
	{
		x_0 = x0;
		x_1 = x1;
	}
	
	for (i = x_0 ; i <= x_1; i++)	//依次画点
	{
		oledDrawPoint(i, y, t);
	}
	oled_refresh_gram();//更新显示
}

/** 
  * @name	void OLED_DrawRow(u8 x0, u8 y, u8 x1, u8 t)
  * @brief  画竖直线
  * @param	x: 0~127	y0: 0~63		y1:	0~63	t:1 填充 0 清空
  * @retval	none
  */
void oledDrawRow(u8 x, u8 y0, u8 y1, u8 t)
{
	 u8 y_0, y_1, i;
	if (y0 > y1)	//判断y0与y1的大小
	{
		y_0 = y1;
		y_1 = y0;
	}
	else
	{
		y_0 = y0;
		y_1 = y1;
	}
	
	for (i = y_0 ; i <= y_1; i++)	//依次画点
	{
		oledDrawPoint(x, i, t);
	}
	oled_refresh_gram();//更新显示
}
/** 
  * @name	void OLED_DrawRow(u8 x0, u8 y, u8 x1, u8 t)
  * @brief  画矩形
  * @param	x0: 0~127	y0: 0~63	x1: 0~127	y1:	0~63	t:1 填充 0 清空
  * @retval	none
  */
void oledDrawRectangle(u8 x0, u8 y0, u8 x1, u8 y1, u8 t)
{
	 u8 y_0, y_1, x_0, x_1, i;
	if (y0 > y1)	//判断y0与y1的大小
	{
		y_0 = y1;
		y_1 = y0;
	}
	else
	{
		y_0 = y0;
		y_1 = y1;
	}
	
	if (x0 > x1)	//判断x0与x1的大小
	{
		x_0 = x1;
		x_1 = x0;
	}
	else
	{
		x_0 = x0;
		x_1 = x1;
	}
	
	for (i = x_0 ; i <= x_1; i++)	//依次画点
	{
		oledDrawPoint(i, y0, t);
		oledDrawPoint(i, y1, t);
	}
	
	for (i = y_0 ; i <= y_1; i++)	//依次画点
	{
		oledDrawPoint(x0, i, t);
		oledDrawPoint(x1, i, t);
	}
	oled_refresh_gram();//更新显示
}
